// System includes
#include <QDebug>

// Local includes
#include "yignetworkchannel.h"
#include "yig.h"

extern bool ONLINE;

float randomFloat(float min, float max)
{
    float r = (float)rand() / (float) RAND_MAX;
    return min + r * (max - min);
}

YigNetworkChannel::YigNetworkChannel(int channelID, Yig *yig) :
    mYig(yig),
    busReserve(3),
    lastPoint(-99,-99)
{
    mChannelID = channelID;
    synthCounter = 0;
    srand((unsigned) time(0));
}

YigNetworkChannel::~YigNetworkChannel()
{
    QHashIterator<QString,YigSynth*> synthIter(synthMap);
    while(synthIter.hasNext())
    {
        delete synthIter.next().value();
    }
    synthMap.clear();

    QHashIterator<QString,YigCable*> cableIter(cableMap);
    while(cableIter.hasNext())
    {
        delete cableIter.next().value();
    }
    cableMap.clear();

    delete synthGroup;
    delete cableGroup;
}

int YigNetworkChannel::getChannelID()
{
    return mChannelID;
}

QHash<QString, YigSynth*> YigNetworkChannel::getSynthMap()
{
    return synthMap;
}

void YigNetworkChannel::createGroups()
{
    synthGroup = new sc::Group();
    cableGroup = new sc::Group(synthGroup, sc::addAfter);
    //cableGroup = mSCLang->newGroup(SCLang::AfterNode, synthGroup->getNodeID());
}

void YigNetworkChannel::requestCreateSynth(QString userName, QString synthName, QPoint point, float param1, float param2)
{
    // Hacky patch for multiple drag and drop triggers in the same spot :/
    if(point != lastPoint)
    {
        //synthCounter++;
        QString key = userName + QString::number(randomFloat(0, 9), 'f', 5);

        if(ONLINE)
        {
            //qDebug() << "ONLINE Synth Creation??";
            QByteArray ba = key.toLocal8Bit();
            QByteArray synthBA = synthName.toLocal8Bit();
            mYig->requestCreateSynth(ba.data(), synthBA.data(), point.x(), point.y(), param1, param2);
        }

        else
        {
            //qDebug() << "OFFLINE Synth Creation??";
            createSynthObject(key, synthName, point, param1, param2);
        }
        lastPoint = point;
    }

    else
    {
        qDebug() << "MULTIPLE DRAG/DROPS";
    }
}

// void YigNetworkChannel::createSynthObject(QString key, QString synthName, QPoint point)
void YigNetworkChannel::createSynthObject(QString key, QString synthName, QPoint point, float param1, float param2)
{
    //synthCounter++;
    //QString key = userName + "ch" + QString::number(mChannelID) + "s" + QString::number(synthCounter);

    if(!synthMap.contains(key))
    {
        //Synth* synth = mSCLang->newSynth(qPrintable(synthName),SCLang::HeadOfGroup, synthGroup->getNodeID());
        sc::Synth* synth = sc::Synth::after(synthGroup, qPrintable(synthName));
        YigSynth* yigSynth = new YigSynth(synth, key);
        int audioInBus = busReserve.acquireAudioBus();
        yigSynth->setAudioInBus(audioInBus);
        yigSynth->setAudioOutBus(audioInBus + 1);
        yigSynth->setModInBus(audioInBus + 2);
        yigSynth->setParam1(param1);
        yigSynth->setParam2(param2);
        synthMap.insert(key, yigSynth);
        emit synthCreated(yigSynth, key, synthName, point, param1, param2);
    }
}

void YigNetworkChannel::destroySynthObject(QString key)
{
    if(synthMap.contains(key))
    {
        YigSynth* yigSynth = synthMap[key];
        yigSynth->free();
        busReserve.releaseAudioBus(yigSynth->getAudioInBus());
        emit synthDestroyed(key);
        //qDebug() << " DELETE: " << key;
        delete yigSynth;
        synthMap.remove(key);  
    }
}

void YigNetworkChannel::updateSynthParam1(QString mapKey, float param1)
{
    if(synthMap.contains(mapKey))
    {
        synthMap[mapKey]->setParam1(param1);
    }
}

void YigNetworkChannel::updateSynthParam2(QString mapKey, float param2)
{
    if(synthMap.contains(mapKey))
    {
        synthMap[mapKey]->setParam2(param2);
    }
}

void YigNetworkChannel::checkSynthAlive(QString mapKey, QString synthName)
{
    if(!synthMap.contains(mapKey))
    {
        qDebug() << "LOCAL CREATE SYNTH DOES THIS MAKE DUPLICATES?";
        //createSynthObject(mapKey, synthName, QPoint(0, 0), 0, 0);
    }
}

void YigNetworkChannel::requestDestroySynth(QString key)
{
    if(ONLINE)
    {
        QByteArray ba = key.toLocal8Bit();
        mYig->requestDeleteSynth(ba.data());
    }

    else
    {
        destroySynthObject(key);
    }
}

void YigNetworkChannel::createCableObject(QString mapKey, QString inputKey, QString outputKey,
                                          YigCableGraphic::CableOutputType outputType)
{
    //Synth* synth = mSCLang->newSynth("YigCable",SCLang::HeadOfGroup, cableGroup->getNodeID());
    sc::Synth* synth = sc::Synth::head(cableGroup, "YigCable");
    int outBus;
    if(outputType == YigCableGraphic::AUDIO)
    {
        outBus = synthMap[outputKey]->getAudioInBus();
    }

    else
    {
        outBus = synthMap[outputKey]->getModInBus();
    }

    YigCable* cable = new YigCable(synth, synthMap[inputKey]->getAudioOutBus(), outBus);
    cableMap.insert(mapKey, cable);
}

void YigNetworkChannel::destroyCableObject(QString mapKey)
{
    if(cableMap.contains(mapKey))
    {
        cableMap[mapKey]->free();
        delete cableMap[mapKey];
        cableMap.remove(mapKey);
    }
}
